//////////////////////////////////////////ok
#include"stdafx.h"
#include "bochs.h"



void  IA32_CPU::push_16(Bit16u value16)
{

  if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b) 
  {
	  /*
     if (!can_push(& sregs[IA32_SEG_REG_SS].cache, ESP, 2)) 
	 {
        exception(IA32_SS_EXCEPTION, 0, 0);
     }
	 */
     write_virtual_word(IA32_SEG_REG_SS, ESP-2, &value16);
     ESP -= 2;
  }
  else
  {
	  /*
     if (!can_push(& sregs[IA32_SEG_REG_SS].cache, SP, 2)) 
	 {
        //BX_INFO(("push_16(): push outside stack limits"));
        exception(IA32_SS_EXCEPTION, 0, 0);
     }
	 */
     write_virtual_word(IA32_SEG_REG_SS, (Bit16u) (SP-2), &value16);
     SP -= 2;
  }

}

/* push 32 bit operand size */
void IA32_CPU::push_32(Bit32u value32)
{
  if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b) 
  {
	  /*
      if (!can_push(& sregs[IA32_SEG_REG_SS].cache, ESP, 4)) 
	  {
        exception(IA32_SS_EXCEPTION, 0, 0);
      }
	  */
      write_virtual_dword(IA32_SEG_REG_SS, ESP-4, &value32);
      ESP -= 4;
  }
  else 
  {
	  /*
      if (!can_push(& sregs[IA32_SEG_REG_SS].cache, SP, 4)) 
	  {
        exception(IA32_SS_EXCEPTION, 0, 0);
      }
	  */
      write_virtual_dword(IA32_SEG_REG_SS, (Bit16u) (SP-4), &value32);
      SP -= 4;
  }
}


void IA32_CPU::pop_16(Bit16u *value16_ptr)
{
  Bit32u temp_ESP;

  if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
    temp_ESP = ESP;
  else
    temp_ESP = SP;

  /*
    if(!can_pop(2) ) 
	{
      exception(IA32_SS_EXCEPTION, 0, 0);
      return;
    }
	*/


  read_virtual_word(IA32_SEG_REG_SS, temp_ESP, value16_ptr);
  if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
    ESP += 2;
  else
    SP += 2;

}

void IA32_CPU::pop_32(Bit32u *value32_ptr)
{
	Bit32u temp_ESP;
	if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
		temp_ESP = ESP;
	else
		temp_ESP = SP;

	/*
    if (!can_pop(4) ) 
	{
      exception(IA32_SS_EXCEPTION, 0, 0);
      return;
    }
	*/

	read_virtual_dword(IA32_SEG_REG_SS, temp_ESP, value32_ptr);
	if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b==1)
		ESP += 4;
	else
		SP += 4;
}


bx_bool  IA32_CPU::can_push(ia32_descriptor_t *descriptor, Bit32u esp, Bit32u bytes)
{

  if (!descriptor->u.segment.d_b)
    esp &= 0x0000ffff;


  if (descriptor->valid==0) 
  {
    return(0);
  }

  if (descriptor->p==0) 
  {
    return(0);
  }


  if (descriptor->u.segment.c_ed) 
  {
    Bit32u expand_down_limit;
    if (descriptor->u.segment.d_b)
      expand_down_limit = 0xffffffff;
    else
      expand_down_limit = 0x0000ffff;

    if (esp==0) 
	{
      return(0);
    }

    if (esp < bytes) 
	{
      return(0);
    }

    if ( (esp - bytes) <= descriptor->u.segment.limit_scaled ) 
	{
      return(0);
    }
    
	if ( esp > expand_down_limit ) 
	{
      return(0);
    }
    return(1);
  }
  else 
  { 
    if (descriptor->u.segment.limit_scaled==0) 
	{
      return(0);
    }

    if (esp==0) 
	{
      if (descriptor->u.segment.d_b && (descriptor->u.segment.limit_scaled==0xffffffff))
        return(1);
      
	  if ((descriptor->u.segment.d_b==0) && (descriptor->u.segment.limit_scaled>=0xffff))
        return(1);
      return(0);
    }

    if ( !descriptor->u.segment.d_b ) 
	{
      esp = ((esp-bytes) & 0xffff) + bytes;
    }

    if (esp < bytes) 
	{
      return(0);
    }

    if ((esp-1) > descriptor->u.segment.limit_scaled) 
	{
      return(0);
    }
    return(1);
  }
}


bx_bool IA32_CPU::can_pop(Bit32u bytes)
{
  Bit32u temp_ESP, expand_down_limit;

  if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b) 
  { 
    temp_ESP = ESP;
    expand_down_limit = 0xFFFFFFFF;
  }
  else 
  { 
    temp_ESP = SP;
    expand_down_limit = 0xFFFF;
  }

  if ( sregs[IA32_SEG_REG_SS].cache.valid==0) 
  {
    return(0); /* never gets here */
  }

  if ( sregs[IA32_SEG_REG_SS].cache.p==0) 
  { 
    return(0);
  }

  if ( sregs[IA32_SEG_REG_SS].cache.u.segment.c_ed) 
  { 
    if ( temp_ESP == expand_down_limit ) 
	{
      return(0);
    }
    
	if ( ((expand_down_limit - temp_ESP) + 1) >= bytes )
      return(1);
    return(0);
  }
  else 
  {
    if ( sregs[IA32_SEG_REG_SS].cache.u.segment.limit_scaled==0) 
	{
    }

    if ( temp_ESP == expand_down_limit ) 
	{
      return(0);
    }

    if ( temp_ESP >  sregs[IA32_SEG_REG_SS].cache.u.segment.limit_scaled ) 
	{
      return(0);
    }

    if ( (( sregs[IA32_SEG_REG_SS].cache.u.segment.limit_scaled - temp_ESP) + 1) >= bytes )
      return(1);

    return(0);
  }
}

void  IA32_CPU::decrementESPForPush(unsigned nBytes, Bit32u *eSP_ptr)
{
	Bit32u eSP;

	if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b)
		eSP = ESP;
	else
		eSP = SP;


    if (!can_push(& sregs[IA32_SEG_REG_SS].cache, eSP, nBytes)) 
	{
        exception(IA32_SS_EXCEPTION, 0, 0);
    }

	eSP -= nBytes;
	if ( sregs[IA32_SEG_REG_SS].cache.u.segment.d_b) 
	{
		ESP = eSP;
		*eSP_ptr = eSP;
	}
	else 
	{
		SP = (Bit16u) eSP;
		*eSP_ptr = SP;
	}
}
